
import { IEquatable } from "../Utils/IEquatable";
import { ActionSegment } from "./ActionSegment";
import { ActionSpec } from "./ActionSpec";
import { IDiscreteActionMask } from "./IDiscreteActionMask";

/**
 * 行为片段(ActionSegment)的包装，在收到 {@link IActionReceiver#OnActionReceived} 时候被调用
 */
export class ActionBuffers implements IEquatable<ActionBuffers> {

    public static empty(): ActionBuffers {
        return new ActionBuffers(ActionSegment.empty, ActionSegment.empty);
    }

    private _continuousActions: ActionSegment = null!;
    get continuousActions(): ActionSegment {
        return this._continuousActions;
    }

    private _discreteActions: ActionSegment = null!;
    public get discreteActions(): ActionSegment {
        return this._discreteActions;
    }

    static fromDiscreteActions(discreteActions: number[]): ActionBuffers {
        return new ActionBuffers(ActionSegment.empty,
            discreteActions == null ? ActionSegment.empty
                : new ActionSegment(discreteActions.map(num => Math.floor(num))));
    }

    constructor(continuousActions?: ActionSegment | Array<number> | ActionSpec, discreteActions?: ActionSegment | Array<number>) {
        if (continuousActions instanceof ActionSpec) {
            this._continuousActions = new ActionSegment(new Array<number>(continuousActions.numContinuousActions));
            this._discreteActions = new ActionSegment(new Array(continuousActions.numDiscreteActions).fill(0));
        } else if (continuousActions instanceof ActionSegment && discreteActions instanceof ActionSegment) {
            this._continuousActions = continuousActions;
            this._discreteActions = discreteActions;
        } else if (continuousActions instanceof Array && discreteActions instanceof Array) {
            this._continuousActions = new ActionSegment(continuousActions);
            this._discreteActions = new ActionSegment(discreteActions);
        }
    }
    equals(other: ActionBuffers): boolean {

        return other.continuousActions.equals(this.continuousActions)
            && other.discreteActions.equals(this.discreteActions);
    }

    static fromActionSpec(actionSpec: ActionSpec, actions: Array<number>): ActionBuffers {
        if (!actions) {
            return ActionBuffers.empty();
        }

        let continuousActionSegment = ActionSegment.empty;
        let discreteActionSegment = ActionSegment.empty;

        let offset = 0;
        if (actionSpec.numContinuousActions > 0) {
            continuousActionSegment = new ActionSegment(actions, 0, actionSpec.numContinuousActions);
            offset += actionSpec.numContinuousActions;
        }

        if (actionSpec.numDiscreteActions > 0) {
            const discreteActions = new Array(actionSpec.numContinuousActions);
            for (let i = 0; i < actionSpec.numContinuousActions; i++) {
                discreteActions[i] = Math.floor(actions[i + offset])
            }
            discreteActionSegment = new ActionSegment(discreteActions);
        }

        return new ActionBuffers(continuousActionSegment, discreteActionSegment);
    }

    clear() {
        this._continuousActions.clear();
        this.discreteActions.clear();
    }

    isEmpty() {
        return this._continuousActions.isEmpty() && this._discreteActions.isEmpty();
    }

}


/**
 * 描述可也从强化学习网络(Reinforcement Learning Network)接受动作的对象的接口
 */
export interface IActionReceiver {

    /**
     * 方法被调用是为了允许对象基于ActionBuffers内容去执行行为
     * @param actionBuffers 包含对象所有行为的数据结构
     */
    onActionReceived(actionBuffers: ActionBuffers): void;


    /**
     * 实现这个方法去修改离散行为(discrete actions)的掩码(masks)。
     * 在使用离散行为时，代理(agent)不会执行被掩盖的行为。
     * @param actionMask 
     */
    writeDiscreteActionMask(actionMask: IDiscreteActionMask): void;

}